package org.jeecg.common.util.sm;

import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;
import org.jeecg.common.util.StreamConsumer;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class GMSSLUtil {
	
	public static final String SM_ENCRYPT = "encrypt";
	
	public static final String SM_DECRYPT = "decrypt";
	
	/**
     * SM2算法生成密钥对
     * @return 密钥对信息
     */
	public static Map<String,String> generateKeypair() throws Exception  {
		X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
		ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
		ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
		keyPairGenerator.init(new ECKeyGenerationParameters(domainParameters, SecureRandom.getInstance("SHA1PRNG")));
		AsymmetricCipherKeyPair asymmetricCipherKeyPair = keyPairGenerator.generateKeyPair();
		 
		//私钥，16进制格式，自己保存，格式如a2081b5b81fbea0b6b973a3ab6dbbbc65b1164488bf22d8ae2ff0b8260f64853
		BigInteger privatekey = ((ECPrivateKeyParameters) asymmetricCipherKeyPair.getPrivate()).getD();
		String privateKeyHex = privatekey.toString(16);
		 
		//公钥，16进制格式，发给前端，格式如04813d4d97ad31bd9d18d785f337f683233099d5abed09cb397152d50ac28cc0ba43711960e811d90453db5f5a9518d660858a8d0c57e359a8bf83427760ebcbba
		ECPoint ecPoint = ((ECPublicKeyParameters) asymmetricCipherKeyPair.getPublic()).getQ();
		String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
		Map<String,String> keypair = new HashMap<>();
		keypair.put("private", privateKeyHex);
		keypair.put("public", publicKeyHex);
		return keypair;
	}
	
	public static String decryptSm2(String publicKey, String privateKey, String cipher) throws Exception {
		return runSm2Cmd(publicKey, privateKey, SM_DECRYPT, cipher);
	}
	
	public static String encryptSm2(String publicKey, String privateKey, String data) throws Exception {
		
		return runSm2Cmd(publicKey, privateKey, SM_ENCRYPT, data);
	}
	
	public static String decryptSm4(String privateKey, String cipher) throws Exception {
		return runSm4Cmd(privateKey, SM_DECRYPT, cipher);
	}
	
	public static String encryptSm4(String privateKey, String data) throws Exception {	
		return runSm4Cmd(privateKey, SM_ENCRYPT, data);
	}
	
	public static String encryptSm3(String hexStr) throws Exception {	
		return runSm3Cmd(hexStr);
	}
	
	public static String runSm2Cmd(String publicKey, String privateKey, String oper, String hexStr) {
		List<String> cmds = sm2CommonCmd(publicKey, privateKey, oper, hexStr);
		String[] cmdArr = cmds.toArray(new String[cmds.size()]);
		String hexData = runAndGetValueWithPipe(cmdArr).trim();
		if(StringUtils.isNotBlank(hexData)) {
			return hexData;
		}else {
			return null;
		}
	}
	
    public static List<String> sm2CommonCmd(String publicKey, String privateKey, String oper, String hexStr) {

    	List<String> cmds = new ArrayList<String>();
    	cmds.add("smXtool");
    	cmds.add("--type=sm2");
    	cmds.add("--action=" + oper);
    	cmds.add("--pub_key=" + publicKey);
    	cmds.add("--pri_key=" + privateKey);
    	if(SM_DECRYPT.equals(oper)) {
    		cmds.add("--enc_data=" + hexStr);
    	}else if(SM_ENCRYPT.equals(oper)) {
    		cmds.add("--dec_data=" + hexStr);
    	}
    	
    	return cmds;
    }
    
	public static String runSm3Cmd(String hexStr) {
		List<String> cmds = sm3CommonCmd(hexStr);
		String[] cmdArr = cmds.toArray(new String[cmds.size()]);
		String hexData = runAndGetValueWithPipe(cmdArr).trim();
		if(StringUtils.isNotBlank(hexData)) {
			return hexData;
		}else {
			return null;
		}
	}
	
    public static List<String> sm3CommonCmd(String hexStr) {

    	List<String> cmds = new ArrayList<String>();
    	cmds.add("smXtool");
    	cmds.add("--type=sm3");
    	cmds.add("--dec_data=" + hexStr);
    	
    	return cmds;
    }
    
	public static String runSm4Cmd(String privateKey, String oper, String hexStr) {
		List<String> cmds = sm4CommonCmd(privateKey, oper, hexStr);
		String[] cmdArr = cmds.toArray(new String[cmds.size()]);
		String hexData = runAndGetValueWithPipe(cmdArr).trim();
		if(StringUtils.isNotBlank(hexData)) {
			return hexData;
		}else {
			return null;
		}
	}
	
    public static List<String> sm4CommonCmd(String privateKey, String oper, String hexStr) {

    	List<String> cmds = new ArrayList<String>();
    	cmds.add("smXtool");
    	cmds.add("--type=sm4");
    	cmds.add("--action=" + oper);
    	cmds.add("--sm4_type=ecb");
    	cmds.add("--pri_key=" + privateKey);
    	if(SM_DECRYPT.equals(oper)) {
    		cmds.add("--enc_data=" + hexStr);
    	}else if(SM_ENCRYPT.equals(oper)) {
    		cmds.add("--dec_data=" + hexStr);
    	}
    	
    	return cmds;
    }
	
	public static String runAndGetValue(String[] command) {
        String result = null;
        Process proc = null;
        try {
            String commandString = "";
            for (String part : command) {
                commandString += part + " ";
            }
            log.debug("Running command: " + commandString);
            Runtime rt = Runtime.getRuntime();
            proc = rt.exec(command);
            StreamConsumer error = new StreamConsumer(proc.getErrorStream());
            StreamConsumer output = new StreamConsumer(proc.getInputStream());
            error.start();
            output.start();
            int returnValue = proc.waitFor();
            output.join();
            if (returnValue != 0) {
                result = error.getReturnValue();
            }

            if (StringUtils.isBlank(result)) {
                result = output.getReturnValue();
            }
        } catch (Exception t) {
            log.error("Running command error :", t.getMessage());
        } finally {
            if (proc != null)
                proc.destroy();
        }
        return result;
    }
    
    public static String runAndGetValueWithPipe(String[] command) {       
        String subCommand = StringUtils.join(command, " ");
        String[] execCmds = {"sh", "-c",  subCommand};
        return runAndGetValue(execCmds);
    }
    
    public static String hexToStr(String hexStr) {
    	return null;
    }

}
